/**
 * 
 */
package instant.openvfs.home.web;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;

import jvs.vfs.FileSystem;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.pm.ProviderInfo;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;

/**
 * @author qiangli
 * 
 */
public abstract class BaseFileContentProvider extends ContentProvider {
	private static final boolean DEBUG = instant.openvfs.home.Debug.DEBUG;
	public class Columns implements android.provider.BaseColumns {
		// _ID
		// _COUNT
		public static final String NAME = "name";

		public static final String ATTRIBUTES = "attributes";
		public static final String CAN_EXECUTE = "can_execute";
		public static final String CAN_READ = "can_read";
		public static final String CAN_WRITE = "can_write";
		//
		public static final String CONTENT = "content";
		public static final String CONTENT_ENCODING = "content_encoding";
		public static final String CONTENT_TYPE = "content_type";
		public static final String DISPLAY_NAME = "display_name";
		public static final String ICON = "icon";
		public static final String IS_DIRECTORY = "is_directory";
		public static final String IS_EMPTY = "is_empty";
		public static final String IS_ENCRYPTED = "is_encrypted";
		public static final String IS_FILE = "is_file";
		public static final String IS_HIDDEN = "is_hidden";
		//
		public static final String IS_LINK = "is_link";
		public static final String IS_LOCAL = "is_local";
		public static final String IS_NATIVE = "is_native";
		public static final String LAST_MODIFIED = "last_modified";
		public static final String LENGTH = "length";

		public static final String PHONETIC_NAME = "phonetic_name";
		public static final String SORT_KEY = "sort_key";
		//
		public static final String URI = "uri";
	}

	public static class ColumnsIndex {
		public static final int _ID = 0;
		public static final int NAME = 1;
		public static final int LENGTH = 2;
		public static final int LAST_MODIFIED = 3;
		public static final int CAN_READ = 4;
		public static final int CAN_WRITE = 5;
		public static final int CAN_EXECUTE = 6;
		public static final int IS_DIRECTORY = 7;
		public static final int IS_FILE = 8;
		public static final int IS_LINK = 9;
		public static final int IS_HIDDEN = 10;
		public static final int IS_EMPTY = 11;
		public static final int IS_LOCAL = 12;
		public static final int IS_NATIVE = 13;
		public static final int IS_ENCRYPTED = 14;
		public static final int SORT_KEY = 15;
		public static final int PHONETIC_NAME = 16;
		public static final int DISPLAY_NAME = 17;
		public static final int ICON = 18;
		public static final int ATTRIBUTES = 19;
		public static final int CONTENT = 20;
		public static final int CONTENT_ENCODING = 21;
		public static final int CONTENT_TYPE = 22;
		public static final int URI = 23;
	}

	public static final String[] PROJECTION = new String[] { Columns._ID,
			Columns.NAME, Columns.LENGTH, Columns.LAST_MODIFIED,
			Columns.CAN_READ, Columns.CAN_WRITE, Columns.CAN_EXECUTE,
			Columns.IS_DIRECTORY, Columns.IS_FILE, Columns.IS_LINK,
			Columns.IS_HIDDEN, Columns.IS_EMPTY, Columns.IS_LOCAL,
			Columns.IS_NATIVE, Columns.IS_ENCRYPTED, Columns.SORT_KEY,
			Columns.PHONETIC_NAME, Columns.DISPLAY_NAME, Columns.ICON,
			Columns.ATTRIBUTES, Columns.CONTENT, Columns.CONTENT_ENCODING,
			Columns.CONTENT_TYPE, Columns.URI };

	/**
	 *
	 */
	private static class TempParcelFileDescriptor extends ParcelFileDescriptor {
		private java.io.File temp = null;

		public TempParcelFileDescriptor(ParcelFileDescriptor fd,
				java.io.File temp) {
			super(fd);
			this.temp = temp;
		}

		@Override
		public void close() throws IOException {
			if (DEBUG) {
				Log.d(TAG, "close: " + temp);
			}
			try {
				super.close();
				temp.delete();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public static final String AUTHORITY = "v:"
			+ BaseFileContentProvider.class.getPackage();

	public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
			+ "/");

	private static long seq = System.currentTimeMillis();

	private static final String TAG = "BaseContentProvider";

	private static final int BUFSIZE = 1024;

	private static String tmpprefix = BaseFileContentProvider.class
			.getSimpleName();

	public static void closeSilently(Closeable c) {
		if (c == null) {
			return;
		}
		try {
			c.close();
		} catch (Throwable t) {
			// do nothing
		}
	}

	public static Uri constructUri(Uri uri) {
		Uri u = Uri.parse(CONTENT_URI + uri.getEncodedPath());
		if (DEBUG) {
			Log.d(TAG, " " + u);
		}
		return u;
	}

	protected static java.io.File createTempFile(File cacheDir)
			throws IOException {
		java.io.File f = new java.io.File(cacheDir, nextTempName());
		f.deleteOnExit();
		return f;
	}

	protected static ParcelFileDescriptor createTempParcelFileDescriptor(
			java.io.File temp) throws FileNotFoundException {
		ParcelFileDescriptor fd = ParcelFileDescriptor.open(temp,
				ParcelFileDescriptor.MODE_READ_ONLY);
		return new TempParcelFileDescriptor(fd, temp);
	}

	synchronized private static String nextTempName() {
		seq++;
		return tmpprefix + seq + ".tmp";
	}

	private java.io.File cacheDir = null;

	@Override
	public void attachInfo(Context context, ProviderInfo info) {
		super.attachInfo(context, info);
	}

	@Override
	public int bulkInsert(Uri uri, ContentValues[] values) {
		throw new UnsupportedOperationException();
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		throw new UnsupportedOperationException();
	}

	@Override
	public String getType(Uri uri) {
		if (DEBUG) {
			Log.d(TAG, "query uri: " + uri);
		}
		return "text/html";
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {
		throw new UnsupportedOperationException();
	}

	@Override
	protected boolean isTemporary() {
		return false;
	}

	@Override
	public boolean onCreate() {
		try {
			Context ctx = getContext();
			cacheDir = new java.io.File(ctx.getCacheDir(), this.getClass()
					.getName());
			cacheDir.mkdirs();
			return true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return true;
	}

	@Override
	public void onLowMemory() {
		super.onLowMemory();
		//
		// clear cache
	}

	@Override
	public AssetFileDescriptor openAssetFile(Uri uri, String mode)
			throws FileNotFoundException {
		ParcelFileDescriptor fd = openFile(uri, mode);
		return (fd != null ? new AssetFileDescriptor(fd, 0, -1) : null);
	}

	@Override
	public ParcelFileDescriptor openFile(Uri uri, String mode)
			throws FileNotFoundException {
		if (DEBUG) {
			Log.d(TAG, "*** open: " + uri + " mode: " + mode);
		}
		try {
			ParcelFileDescriptor pfd = handle(uri, mode);
			return pfd;
		} catch (Exception e) {
			e.printStackTrace();
		}
		throw new FileNotFoundException(uri + "");
	}

	/**
	 * 
	 * @param uri
	 *            request uri
	 * @param mode
	 *            read/write
	 * @return content of the uri
	 * @throws Exception
	 */
	protected ParcelFileDescriptor handle(Uri uri, String mode)
			throws Exception {
		Object data = getData(uri, mode);
		if (data != null) {
			if (data instanceof java.io.File) {
				return createParcelFileDescriptor((java.io.File) data);
			}
			if (data instanceof String) {
				return createParcelFileDescriptor((String) data);
			}
			if (data instanceof InputStream) {
				return createParcelFileDescriptor((InputStream) data);
			}
			if (data instanceof x.java.io.File) {
				java.io.File nf = FileSystem.getFileSystem().nativeFile((x.java.io.File)data);
				return createParcelFileDescriptor(nf);
			}
		}
		return null;
	}

	protected ParcelFileDescriptor createParcelFileDescriptor(InputStream data)
			throws IOException {
		File f = createTempFile(cacheDir);
		OutputStream os = new FileOutputStream(f);
		BufferedOutputStream bos = new BufferedOutputStream(os);
		copy(data, bos);
		data.close();
		bos.close();
		return createTempParcelFileDescriptor(f);
	}

	protected ParcelFileDescriptor createParcelFileDescriptor(String data)
			throws Exception {
		File f = createTempFile(cacheDir);
		FileWriter fw = new FileWriter(f);
		BufferedWriter bw = new BufferedWriter(fw);
		bw.write(data);
		bw.close();
		return createTempParcelFileDescriptor(f);
	}

	protected ParcelFileDescriptor createParcelFileDescriptor(java.io.File file)
			throws Exception {
		final int mode = ParcelFileDescriptor.MODE_READ_ONLY;
		ParcelFileDescriptor fd = ParcelFileDescriptor.open(file, mode);
		return fd;
	}

	protected abstract Object getData(Uri uri, String mode) throws Exception;

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		if (DEBUG) {
			Log.d(TAG, "query: "
					+ uri
					+ " projection: "
					+ (projection == null ? "" : Arrays.asList(projection))
					+ " selection: "
					+ selection
					+ " args: "
					+ (selectionArgs == null ? "" : Arrays
							.asList(selectionArgs)) + " sortOrder: "
					+ sortOrder);
		}
		try {
			FileMatrixCursor c = getMatrixCursor(uri, projection, selection,
					selectionArgs, sortOrder);
			c.setNotificationUri(getContext().getContentResolver(), uri);
			return c;
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return null;
	}

	abstract protected FileMatrixCursor getMatrixCursor(Uri uri,
			String[] projection, String selection, String[] selectionArgs,
			String sortOrder);

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		throw new UnsupportedOperationException();
	}

	protected void copy(InputStream is, Appendable out) throws IOException {
		BufferedReader r = new BufferedReader(new InputStreamReader(is),
				BUFSIZE);
		while (true) {
			String l = r.readLine();
			if (l == null) {
				break;
			}
			out.append(l);
		}
	}

	private void copy(InputStream is, OutputStream os) throws IOException {
		byte[] buf = new byte[BUFSIZE];
		while (true) {
			int read = is.read(buf);
			if (read == -1) {
				break;
			}
			if (read > 0) {
				os.write(buf, 0, read);
			}
		}
		os.flush();
	}
}
